package com.design.sigleton;

public class SingletonPattern {
    public static void main(String[] args) {
        Singleton singleton = Singleton.getInstance();
        System.out.println(singleton);
        Singleton singleton2 = Singleton.getInstance();
        System.out.println(singleton2);
    }
}

/**
 * 单例模式
 * 饿汉式单例设计模式
 * 特点：线程安全，类加载时创建实例，实例唯一
 */
class Singleton {
    private static Singleton singleton = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return singleton;
    }
}

/**
 * 懒汉式单例设计模式
 * 特点：线程不安全，类加载时创建实例，实例唯一
 */
class Singleton2 {
    // 这里为什么要用 volatile 修饰？
    // 不加的话会出现：A线程进入synchronized 代码块，但是还没有赋值，这个时候B线程进入发现singleton2不为null，直接返回空对象。
    // 1.volatile 修饰的变量在多线程下，每次都会从主内存中读取变量的值，从而保证可见性
    // 2.volatile 修饰的变量在每次被赋值后，都会立即更新到主内存中，从而保证可见性
    // 3.volatile 修饰的变量在每次被读取后，都会从主内存中读取变量的值，从而保证可见性
    private static volatile Singleton2 singleton2;
    private Singleton2() {
    }

    /**
     * <h1>双重检验锁：</h1>在多线程环境下，只有第一次创建实例，后续的线程都会直接返回已经创建的实例
     * 缩减了 synchronized 的开销和范围，从而提高性能
     *
     * <h2>性能差的：示例如下</h2>
     * <p>
     *     public static synchronized Singleton2 getInstance() {
     *         if (singleton2 == null) {
     *             singleton2 = new Singleton2();
     *         }
     *         return singleton2;
     *     }
     * </p>
     * @return
     */
    public static Singleton2 getInstance() {
        if (singleton2 == null) {
            synchronized (Singleton2.class) {
                if (singleton2 == null) {
                    // 1、开辟一个空间
                    // 2、初始化
                    // 3、将初始化的值赋值给 singleton2
                    // jvm会优化执行顺序 比如 1->3->2
                    // 第二个线程进行非空判断的时候发现已经有值了，但是它还没有初始化，拿到一个半成品对象
                    // volatile 保证内存可见性
                    singleton2 = new Singleton2();
                }
            }
        }
        return singleton2;
    }
    // 每次都要加锁，性能差
//    public static synchronized Singleton2 getInstance() {
//        if (singleton2 == null) {
//            singleton2 = new Singleton2();
//        }
//        return singleton2;
//    }
}
